import { Subject } from "./subject";
import Utils from "./utils";

//遮挡声音。控制声音大小
export class SoundGrille {
    factor = 1.0;
    key = "";
};

export abstract class SoundGrilleNode {
    grilles: SoundGrille[] = [];
    addGrille(key, factor) {
        if (this.grilles.find(ele => ele.key === key)) {
            return;
        }
        let sg = new SoundGrille();
        sg.key = key;
        sg.factor = factor;
        this.grilles.push(sg);
        this.onGrilleAdd(sg);
    }

    getGrilleFactor() {
        let factor = 1.0;
        this.grilles.forEach(sg => {
            factor *= sg.factor;
        });
        return factor;
    }
    delGrille(key) {
        let ind = this.grilles.findIndex(ele => ele.key === key);
        if (ind !== -1) {
            this.onGrilleRemove(this.grilles.splice(ind, 1)[0]);
        }
    }
    abstract onGrilleAdd(sg: SoundGrille);
    abstract onGrilleRemove(sg: SoundGrille);
}

/**
 * 播放数据
 * @example
 * let cpInfo = SoundManager.ins.playClip( clip );
 * cpInfo.onEnd( ()=>{ //完成播放 } );
 * cpInfo.volume = 0.6;
 */
export class ClipPlayingInfo extends SoundGrilleNode {
    protected _mgr: SoundManager = null;
    /** 
     * valid
     */
    protected _valid: boolean = true;
    get isValid() {
        return this._valid;
    }
    /**
     * 在audioEngine 中的id
     */
    id: number = 0;
    /**
     * playName
     */
    playName: string = "";
    /**
     * 独立的音量控制，范围 0-1
     */
    /**
     * 独立的音量控制, 范围 0-1
     */
    protected _volume: number = 1;
    get volume() {
        return this._volume;
    }
    set volume(val) {
        if (this._volume == val) {
            return;
        }
        this._volume = val;
        if (this._volume > 1) {
            this._volume = 1;
        }
        else if (this._volume < 0) {
            this._volume = 0;
        }
        cc.audioEngine.setVolume(this.id, this._mgr.volume * this._volume * this.getGrilleFactor());
    }
    get time() {
        return cc.audioEngine.getCurrentTime(this.id);
    }

    set time(val: number) {
        cc.audioEngine.setCurrentTime(this.id, val);
    }

    /**
     * 正在播放的音频
     */
    clip: cc.AudioClip = null;
    /**
     * 是否循环
     */
    loop: boolean = false;
    protected _onEnd: Function = null;
    constructor(mgr: SoundManager, clip: cc.AudioClip, loop: boolean, volumeOffset: number, id: number, playName: string) {
        super();
        Object.assign(this, { _mgr: mgr, clip, id, _volume: volumeOffset, loop, playName });
    }
    onEnd(func: Function) {
        this._onEnd = func;
    }
    stop() {
        SoundManager.ins.stopClip(this.id);
    }
    dispose() {
        this._valid = false;
        this._onEnd && this._onEnd();
    }
    async() {
        return new Promise<void>(ok => {
            this.onEnd(ok);
        });
    }

    onGrilleAdd(sg: SoundGrille) {
        cc.audioEngine.setVolume(this.id, this._mgr.volume * this._volume * this.getGrilleFactor());
    }
    onGrilleRemove(sg: SoundGrille) {
        cc.audioEngine.setVolume(this.id, this._mgr.volume * this._volume * this.getGrilleFactor());
    }
};

/** 声音播放器
 *  @example
 *   //在soundManager库中用name查找音频Clip播放。
 *   SoundManager.ins.playClip("click").onEnd(()=>{
 *      //播放完毕
 *   });
 *   //传入一个clip音频播放。
 *   SoundManager.ins.playClip( clip ).onEnd(()=>{
 *      //播放完毕
 *   });
 *   //播放背景音乐（循环）
 *   let cpInfo = SoundManager.ins.playClip( clip, true, 0.7 );
 *   cpInfo.onEnd(()=>{
 *      //播放完毕
 *   });
 *   //把背景音乐的音量设置成60%
 *   cpInfo.volume = 0.6;
 */
export default class SoundManager extends Subject {
    private __mute: number = 0;
    get mute(): boolean {
        return !!this.__mute;
    }
    _volume: number = 1;
    playingList: Map<number, ClipPlayingInfo> = new Map();


    /**
     * 总音量, 范围0-1
     * 用法:
     * SoundManager.ins.volume = 0.8;
     */
    get volume() {
        return this._volume;
    }
    set volume(val: number) {
        if (this._volume == val) {
            return;
        }
        this._volume = val;
        if (this._volume > 1) {
            this._volume = 1;
        }
        else if (this._volume < 0) {
            this._volume = 0;
        }

        this.playingList.forEach((cpInfo) => {
            cc.audioEngine.setVolume(cpInfo.id, this.volume * cpInfo.volume);
        });
    }

    /**
     * 播放cc.AudioClip
     */
    playClip(clip: cc.AudioClip | string, loop: boolean = false, volumeOffset: number = 1): ClipPlayingInfo {
        let playName = "";
        if (typeof clip === "string") {
            playName = clip;
            if (!this.getClip(clip)) {
                cc.log(`SoundManager::playSound, error: 在clipList中没有找到${clip}`);
                return;
            }
            clip = this.getClip(clip);
        }
        else {
            playName = clip.name;
        }

        let id = cc.audioEngine.play(clip, loop, volumeOffset * this.volume);
        let cpInfo = new ClipPlayingInfo(this, clip, loop, volumeOffset, id, playName);
        this.playingList.set(
            id,
            cpInfo
        );

        cc.audioEngine.setFinishCallback(id, () => {
            if (!this.playingList.has(id)) {
                return;
            }
            let disposeInfo = this.playingList.get(id);
            disposeInfo.dispose();
            this.playingList.delete(id);

        });

        return cpInfo;
    }

    /**停止播放某个声音 */
    stopClip(id: ClipPlayingInfo | number) {
        if (id instanceof ClipPlayingInfo) {
            id = id.id;
        }

        if (!this.playingList.has(id)) {
            return;
        }
        let cpInfo = this.playingList.get(id);
        cpInfo.dispose();
        this.playingList.delete(id);
        cc.audioEngine.stop(id);
    }
    /** 停止播放声音 */
    stopClipByName(name: string) {
        this.playingList.forEach(info => {
            if (info.playName == name) {
                this.stopClip(info);
                return false;
            }
        })
    }
    /**停止所有 */
    stopAllClip() {
        this.playingList.forEach(cpInfo => {
            cpInfo.dispose();
            cc.audioEngine.stop(cpInfo.id);
        });
        this.playingList.clear();
    }

    getClip(name: string): cc.AudioClip {
        return Utils.loader.getAsset(name, "sounds");
    }

    toggleMute() {
        this.setMute(this.volume);
    }
    setMute(vol: number) {
        let oldVol = this.__mute;
        this.__mute = vol;
        if (this.mute) {
            this.volume = 0;
        }
        else {
            this.volume = oldVol;
        }
    }

    private static __ins = null;
    static get ins(): SoundManager {
        if (!this.__ins) {
            this.__ins = new SoundManager();
        }
        return this.__ins;
    }
};